/****************************************************************************
 * drivers/timers/goldfish_timer.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/spinlock.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifndef getreg32
#define getreg32(a)      (*(volatile uint32_t *)(a))
#endif

#ifndef putreg32
#define putreg32(v,a)    (*(volatile uint32_t *)(a) = (v))
#endif

#define GOLDFISH_TIMER_TIME_LOW         0x0  /* Get current time, then return low-order 32-bits. */
#define GOLDFISH_TIMER_TIME_HIGH        0x4  /* Return high 32-bits from previous TIME_LOW read. */
#define GOLDFISH_TIMER_ALARM_LOW        0x8  /* Set low 32-bit value of alarm, then arm it. */
#define GOLDFISH_TIMER_ALARM_HIGH       0xc  /* Set high 32-bit value of alarm. */
#define GOLDFISH_TIMER_IRQ_ENABLED      0x10 /* Enable interrupts. */
#define GOLDFISH_TIMER_CLEAR_ALARM      0x14 /* Clear alarm. */
#define GOLDFISH_TIMER_ALARM_STATUS     0x18 /* Return 1 if alarm is armed, 0 if not. */
#define GOLDFISH_TIMER_CLEAR_INTERRUPT  0x1c /* Clear interrupt. */

/****************************************************************************
 * Private Types
 ****************************************************************************/

/* This structure provides the private representation of the "lower-half"
 * driver state structure.  This structure must be cast-compatible with the
 * oneshot_lowerhalf_s structure.
 */

struct goldfish_timer_lowerhalf_s
{
  struct oneshot_lowerhalf_s lh;        /* Lower half operations */
  uintptr_t                  base;      /* Base address of registers */
  spinlock_t                 lock;      /* Lock for setting timer */
  spinlock_t            read_lock;      /* Lock for reading time */
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static clkcnt_t goldfish_timer_max_delay(struct oneshot_lowerhalf_s *lower);
static clkcnt_t goldfish_timer_current(struct oneshot_lowerhalf_s *lower_);
static void goldfish_timer_start_absolute(struct oneshot_lowerhalf_s *lower_,
                                          clkcnt_t expected);
static void goldfish_timer_start(struct oneshot_lowerhalf_s *lower_,
                                 clkcnt_t delta);
static void goldfish_timer_cancel(struct oneshot_lowerhalf_s *lower_);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct oneshot_operations_s g_goldfish_timer_ops =
{
  .current        = goldfish_timer_current,
  .start          = goldfish_timer_start,
  .start_absolute = goldfish_timer_start_absolute,
  .cancel         = goldfish_timer_cancel,
  .max_delay      = goldfish_timer_max_delay,
};

static struct goldfish_timer_lowerhalf_s g_goldfish_timer_lowerhalf =
{
  .lh.ops = &g_goldfish_timer_ops
};

/****************************************************************************
 * Inline Functions
 ****************************************************************************/

static inline_function uint64_t goldfish_timer_get_nsec(uintptr_t base)
{
  uint32_t l32 = getreg32(base + GOLDFISH_TIMER_TIME_LOW);
  uint32_t h32 = getreg32(base + GOLDFISH_TIMER_TIME_HIGH);

  return ((uint64_t)h32 << 32) | l32;
}

static inline_function
void goldfish_timer_set_nsec(uintptr_t base, uint64_t expected)
{
  putreg32(expected >> 32, base + GOLDFISH_TIMER_ALARM_HIGH);
  putreg32(expected, base + GOLDFISH_TIMER_ALARM_LOW);
}

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static int goldfish_timer_interrupt(int irq, FAR void *context,
                                    FAR void *arg)
{
  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;

  /* Only one core can handle the IRQ, so lock is not required. */

  putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_INTERRUPT);

  /* Then perform the callback */

  oneshot_process_callback(&lower->lh);

  return 0;
}

static clkcnt_t goldfish_timer_max_delay(struct oneshot_lowerhalf_s *lower)
{
  return UINT64_MAX;
}

static clkcnt_t goldfish_timer_current(struct oneshot_lowerhalf_s *lower_)
{
  /* Do not remove the read lock here, because the access to the
   * GOLDFISH_TIMER_TIME_LOW will update the whole GOLDFISH_TIMER_TIME.
   */

  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
  irqstate_t flags = spin_lock_irqsave(&lower->read_lock);
  uint64_t   nsec  = goldfish_timer_get_nsec(lower->base);
  spin_unlock_irqrestore(&lower->read_lock, flags);
  return nsec;
}

static void goldfish_timer_start_absolute(struct oneshot_lowerhalf_s *lower_,
                                          clkcnt_t expected)
{
  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
  irqstate_t flags = spin_lock_irqsave(&lower->lock);

  goldfish_timer_set_nsec(lower->base, expected);

  spin_unlock_irqrestore(&lower->lock, flags);
}

static void goldfish_timer_start(struct oneshot_lowerhalf_s *lower_,
                                 clkcnt_t delta)
{
  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
  irqstate_t flags = spin_lock_irqsave(&lower->lock);

  delta += goldfish_timer_get_nsec(lower->base);

  goldfish_timer_set_nsec(lower->base, delta);

  spin_unlock_irqrestore(&lower->lock, flags);
}

static void goldfish_timer_cancel(struct oneshot_lowerhalf_s *lower_)
{
  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;
  irqstate_t flags = spin_lock_irqsave(&lower->lock);

  /* Disable the timer and clear the interrupt. */

  putreg32(1, lower->base + GOLDFISH_TIMER_CLEAR_ALARM);

  spin_unlock_irqrestore(&lower->lock, flags);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

FAR struct oneshot_lowerhalf_s *
goldfish_timer_initialize(uintptr_t base, int irq)
{
  FAR struct goldfish_timer_lowerhalf_s *lower = &g_goldfish_timer_lowerhalf;

  lower->base = base;

  spin_lock_init(&lower->lock);
  spin_lock_init(&lower->read_lock);

  oneshot_count_init(&lower->lh, NSEC_PER_SEC);

  /* Reset the timer registers. */

  putreg32(1, base + GOLDFISH_TIMER_CLEAR_ALARM);
  putreg32(1, base + GOLDFISH_TIMER_CLEAR_INTERRUPT);
  putreg32(1, base + GOLDFISH_TIMER_IRQ_ENABLED);

  irq_attach(irq, goldfish_timer_interrupt, lower);
  up_enable_irq(irq);

  return (struct oneshot_lowerhalf_s *)lower;
}
